查看原文
其他

某车联网APP加固分析

侬本多情 看雪学苑 2022-09-06


本文为看雪论坛优秀文章

看雪论坛作者ID:侬本多情





分析前准备


目标App:比亚迪汽车 6.1.0
工具:Cydia、Frida、Objection、Storm Sniffer(抓包)





抓包


  1. 首先打开Storm Sniffer,该AppStore可下载。(使用fiddler也可以)

  2. 目标App开启了SSLPinning,导致无法抓包。

通过objection绕过SSLPinning

objection -g 比亚迪汽车 exploreios sslpinning disable --quiet

接下来可以看到抓包内容了。

软件提示:账号密码错误

https://dilinkappserver.byd.com/app/auth/loginUser-Agent: bi ya di qi che/6.0.1 (iPhone; iOS 14.0; Scale/3.00)Content-Type: application/jsonHost: dilinkappserver.byd.com {"request":"FESWLgT0LjY\/GjPQmt4xYvDWajog909VYoUcLQ8TE7E\/F4U9GwHtbmfY9uZbmqPnGW2ys0NRnezIjTETA64wpvfP3aZV+caLdK8fsLovfxoacBp5hIkvlS4hTEYkl8XfIfashQDMOdYmm8W89ZBwpFF+Ipd9hFy7NGHDom4SgXQ5uE7Io8jFnoGiz5CiQ9B3K0NbCSOkSbbwJi14iFi15sy3car\/W3xMNUZuo7\/vNE7Dz\/eHwxroJ7L+DK\/6yXbNLRHMHRtXJykfnD31ShNTEqzkF+Fp2Z6jUuFFpn4B7SB4uJYWe5cJ9g3gDIOEifHEhhD3LCQfOEKppgisduO8el4rEGFaKPx1bzOAIk0W1qPXk0LbVCFPQYFpypUB8Rrn2hBBsA8cepRBBtH73jeRcvrRMBc0gK+YRw4uq8yd44bNoEpSt1L5YOJBelSxmp9WOhkGOjt5cPCHij1ptaO9gp1d7XAg4+MhpzXoluB8aPi8A28stX04l+HdU6lNU0pUGYtFVRbTvYV4KjseXQ5wlRavwFMgv3c61WUMznyLmtI8zdheCVdr3vGG1nNvGI7cqMHdw8jvdYFCe9jr3eu1nG2uHakh7PHDMyEDpEh7WSJvVod+0sleiS+vhv5xnNhXUOtBtjCUcLSZToY6YJky9O4E8WDOGg2j5kvCaHrY\/bgSGd87O7sCkmrjeYEJj5BSV95tlI2Ky1GlTRvG4jLQ800QuXAJyXQJpf9uO5lbmV2yPJ7OcKbRoaRu7rhMj4yok9dPEY5bIJeMFptv+bBTKgRh18R5ZecUdpyx8PrVY7FvTEihzH9z+ACGIMNgw7MZIVRPBqLh\/zXK1JfY3pPblvLVhmbeWQ6ETTC2eEWDmAcBovF+Mpj\/M2ybwRdFGLATHZJUQCobYS+02Jxfd0\/ODHpOQ7c2RJwZh6aijIEeh3Ohj1weh\/ZNAK7189yi3n810Bne0ld\/Vo11Jk7hSSQ9z1vCZijCzbDb4Yuw+jsnM\/CPS0OBMMQqIsuz9TPJ\/89SP103sVv01AOXWUM0OB97POJpQ8Bnfjusf8x7CKURlyj4hl6NHksSAGeOmPv96B\/X\/xEARr3Lq7K0RnVFjqFJUsXVVJavggfrhexl7xgvU7MqOPZ24ZiMmOTKTKP+WVsykcraeoP9+yuYkhdMLPEBelpnkoHOuHvpqojhOcJvN8pu\/tRclZO6KGuRD8I7M3J2+mMo8ZTNQUB7oYVR\/UfIKJ5+aIybJklZ1HTb5nV\/D1tDgWRN5JDvndJ78cEi6s8Hi9gsZjglN1JNaQ3vShP7RuLSYdRqDO7e27cSkibTH4YW6PsHPHhqX2g4ev8WTEbRIAsiVJTOuJqNaPaoX3oDMviBz5aZ0iu6XwRwbojibWBCOABktvzVNgngxxeyPmyJlVQ5H5ehn\/iNNZC4RRz0FqhetTfWdQWXnRqicq+tiujO478mQQVKTDKvhi6y0uL2g2SuR81QJ0YmhlCFxqFWX2ACpro\/4HM5J0EdJvboVwrR70CEBa6pqwsvCSCQCltwM+EDH+OYxt34oL1ngY7+fsm1oZcyEvn3\/snVZcd97klBzdl5oCyj3ANtoDIQua35u2ENXbPc8CqIIlcbPVsmF5U1ESRs5khYlDjBH6qvpZBoA4a36pgMZVu8XKpk7c+bOIB0WrZcMSaIF+r2x0Jg8ZKjrZ\/AwhoRDHAtEIQNDLNkPDvjoEcoI0CF3\/p\/AVyjko9aqtatyNZE7i0MQ4SUdamyogd428NFygGgq5JtoSe3XXL\/3hWxlbTGqESnPXKjh7jTG5FQxy6grBp7lVN2DSW2yNkvVOrtVaRjIbTzJd7\/oiUE29Yx1l\/1tPpJTq0CDSa2sTGgYHyPkppcoA3OXLiEGuQC0lKuvuahyAh956uueAi7DvMUWzPemfszzr6Bd35RucQE5tFjzQ19QIIDGhxLHMfweQHO2jEUuTddqRbnO3ZVyM8jZpcNBwMKbuqjk"} {"response":"Fel63A3fJsBnqDNoqrf3sNRYntlgl2bMzWrA56aEQZRggnMBao9z1ds1G/TqV2TMwrRu1wovdv5AwWkDlpdqO0A=="}





砸壳


使用 frida-ios-dump进行砸壳dump出ipa文件,找到包内二进制文件,拖入IDA中分析。





加密点


4.1 思路:Frida hook -[NSURLRequest initWithURL:] 打印其调用堆栈
try { var className = "NSURLRequest"; var funcName = "- initWithURL:"; var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]'); Interceptor.attach(hook.implementation, { onEnter: function(args) { console.log(colors.green,"NSURLRequest with URL: ",colors.resetColor + ObjC.Object(args[2])); console.log("\nBacktrace:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n")); }, }); } catch (error) { console.log(colors.red,"[!] Exception: ",colors.resetColor + error.message); }

输出如下:
[iPhone::比亚迪汽车 ]-> NSURLRequest with URL: https://dilinkappserver.byd.com/app/auth/loginBacktrace:0x1057d943c /private/var/containers/Bundle/Application/48133A5D-90E1-494C-8472-79C2B5DE912B/比亚迪汽车.app/比亚迪汽车!-[AFHTTPRequestSerializer requestWithMethod:URLString:parameters:error:]0x1057d106c /private/var/containers/Bundle/Application/48133A5D-90E1-494C-8472-79C2B5DE912B/比亚迪汽车.app/比亚迪汽车!-[AFHTTPSessionManager dataTaskWithHTTPMethod:URLString:parameters:headers:uploadProgress:downloadProgress:success:failure:]0x1057d0780 /private/var/containers/Bundle/Application/48133A5D-90E1-494C-8472-79C2B5DE912B/比亚迪汽车.app/比亚迪汽车!-[AFHTTPSessionManager POST:parameters:headers:progress:success:failure:]0x1049e1e24 /private/var/containers/Bundle/Application/48133A5D-90E1-494C-8472-79C2B5DE912B/比亚迪汽车.app/比亚迪汽车!+[AFNRequestUtls postRequestWithInfo:successBlock:failedBlock:]0x104b4d270 /private/var/containers/Bundle/Application/48133A5D-90E1-494C-8472-79C2B5DE912B/比亚迪汽车.app/比亚迪汽车!+[BYDHttpsRequestUtils httpsPostRequestWithRequestRequestInfo:successBlock:failedBlock:]0x1048f48fc /private/var/containers/Bundle/Application/48133A5D-90E1-494C-8472-79C2B5DE912B/比亚迪汽车.app/比亚迪汽车!-[BYDUserModel doLoginSuccess:failed:]0x104a4c4f8 /private/var/containers/Bundle/Application/48133A5D-90E1-494C-8472-79C2B5DE912B/比亚迪汽车.app/比亚迪汽车!-[BYDLoginVC doLogin]...

根据AFHTTPRequestSerializer可以判断出该App使用了 AFNetwoking库(https://github.com/AFNetworking/AFNetworking)。
 
继续hook -[AFHTTPRequestSerializer requestWithMethod:URLString:parameters:error:] 获取parameters参数,内容为加密后参数。
 
可以判断出加密点在+ [BYDHttpsRequestUtils httpsPostRequestWithRequestRequestInfo:successBlock:failedBlock:] 方法内。
 
4.2 ida 查看+ [BYDHttpsRequestUtils httpsPostRequestWithRequestRequestInfo:successBlock:failedBlock:]
 
这里使用Hopper反编译出现了一些问题(这里不知道Hopper如何更改 函数范围,有大佬知道可以留言)。
看一眼CFG图。控制流混淆,稳住,问题不大,先看伪代码试试。
 
这里创建了网络请求 requestInfo实例,找一下设置参数 的函数调用。
 
这里找到了,+[BYDDataManager entryRequestInfo:]这个方法返回加密内容,传入的v6是明文消息字典的aes加密,密钥为$key+$value拼接sha1(先挖个坑,这里讲起来太复杂),再跟进一下康康。
 
离谱了。再继续看看伪代码:
看到request字符串了,上面有个BangSafeSDK可以确定是梆梆企业的加密了,再继续深入看看+[BangSafeSDK checkcode:dataStyle:],结果utf8 编码、获取当前时间,最终调用了sub_1023BE0D4。
 
这里本来想看看sub_1023BE0D4的CFG,IDA直接给报错了,节点太多。

怎么办呢?盲猜一手控制流混淆(已经习惯了),上trace!
 
Frida Stalker trace一下调用地址,感谢misskings大佬。
/// <reference path="frida-gum.d.ts" /> //+[BYDHttpsRequestUtils httpsPostRequestWithRequestRequestInfo:successBlock:failedBlock:]var target = ObjC.classes['BYDHttpsRequestUtils']['+ httpsPostRequestWithRequestRequestInfo:successBlock:failedBlock:']console.log(target.implementation) var m = Process.getModuleByAddress(target.implementation)var vm = m.base.add(0x23BE0D4)//注意ida加载二进制文件的基地址var baseAddr = m.baseconsole.log(vm);Interceptor.attach(vm, { onEnter: function (args) { console.log("enter") this.tid = Process.getCurrentThreadId() console.log(this.tid); Stalker.follow(this.tid, { events: { // 暂时不需要这些 events call: false, ret: false, exec: false, block: false, compile: false }, transform: function (iterator) { var instruction = iterator.next(); const startAddress = instruction.address; // 从ida里面 找到 sub_1023BE0D4 函数的地址范围,alt+p var is = startAddress.compare(baseAddr.add(0x23CBB6C))<0 && startAddress.compare(baseAddr.add(0x23BE0D4))>=0; do{ if(is){ console.log(instruction.address.sub(baseAddr).add(0x100000000) + ":" + instruction); } iterator.keep(); } while ((instruction = iterator.next()) !== null); } }) }, onLeave: function (ret) { Stalker.unfollow(this.tid) }})

trace 的指令如下:
0x1023be0e4:stp x20, x19, [sp, #0x40]0x1023be0e8:stp x29, x30, [sp, #0x50]0x1023be0ec:add x29, sp, #0x500x1023be0f0:sub sp, sp, #0x2200x1023be0f4:sub x9, x29, #0x980x1023be0f8:adrp x8, #0x106f3d0000x1023be0fc:ldr x8, [x8, #0xd70]0x1023be100:ldr x8, [x8]0x1023be104:str x8, [x9, #0x40]0x1023be108:mov w8, #0x12c0x1023be10c:cmp x0, #00x1023be110:str w8, [sp, #0x130]0x1023be114:mov w8, #0x2e0x1023be118:mov w9, #0x4c0x1023be11c:csel w8, w9, w8, eq0x1023be120:stp w8, w2, [sp, #0xb0]0x1023be124:cmp w2, #40x1023be128:mov w8, #0x530x1023be12c:csel w8, w9, w8, eq0x1023be130:str w8, [sp, #0xac]0x1023be134:mov w8, #0x120x1023be138:mov w9, #0x340x1023be13c:csel w8, w9, w8, eq......

配合ida python脚本,去除无用分支:
import sarkimport idcimport sysdef patch_code(addr,code): for i in range(len(code)): idc.patch_byte(addr+i,code[i]) #将指定地址的字节修改成nopdef nop(addr): nop_code=[0x1f,0x20,0x03,0xd5] patch_code(addr,nop_code) def main(): logfilename="./log.txt" #定义要处理函数的起始和终止范围 start=0x1023BE0D4 end=0x1023CBB6C addrs=[] #将所有执行的汇编地址读取进来 with open(logfilename,"r") as logfile: lines=logfile.readlines() for line in lines: addrs.append(int(line.replace("\n","").split(':')[0],16)) #所有执行过的汇编地址修改一下颜色。高亮起来便于查看 for addr in addrs: line=sark.line.Line(addr) line.color=0x00ffff #获取到目标函数内的所有汇编地址 funcLines=sark.lines(start,end) for line in funcLines: #如果该行是code则判断颜色是否被我们标注成有效代码。 if line.type=="code": if line.color!=0x00ffff: nop(line.ea) if __name__=="__main__": main()

再次查看CFG图:


不会,遂放弃。
挖坑了,其实还是控制流混淆,先分析到这里吧。




看雪ID:侬本多情

https://bbs.pediy.com/user-home-855439.htm

*本文由看雪论坛 侬本多情 原创,转载请注明来自看雪社区



# 往期推荐

1.CobaltStrike ShellCode详解

2.Android APP漏洞之战——SQL注入漏洞初探

3.House of apple 一种新的glibc中IO攻击方法

4.so文件分析的一些心得

5.从PWN题NULL_FXCK中学到的glibc知识

6.指令级工具Dobby源码阅读






球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存